/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
define(["require", "exports", "path", "./utils/is", "./utils/api", "./utils/configuration", "./utils/fileSchemes", "./utils/tsconfig"], function (require, exports, path, is, api_1, configuration_1, fileSchemes, tsconfig_1) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    class CallbackMap {
        constructor() {
            this.callbacks = new Map();
            this.pendingResponses = 0;
        }
        destroy(e) {
            for (const callback of this.callbacks.values()) {
                callback.e(e);
            }
            this.callbacks.clear();
            this.pendingResponses = 0;
        }
        add(seq, callback) {
            this.callbacks.set(seq, callback);
            ++this.pendingResponses;
        }
        fetch(seq) {
            const callback = this.callbacks.get(seq);
            this.delete(seq);
            return callback;
        }
        delete(seq) {
            if (this.callbacks.delete(seq)) {
                --this.pendingResponses;
            }
        }
    }
    class RequestQueue {
        constructor() {
            this.queue = [];
            this.sequenceNumber = 0;
        }
        get length() {
            return this.queue.length;
        }
        push(item) {
            this.queue.push(item);
        }
        shift() {
            return this.queue.shift();
        }
        tryCancelPendingRequest(seq) {
            for (let i = 0; i < this.queue.length; i++) {
                if (this.queue[i].request.seq === seq) {
                    this.queue.splice(i, 1);
                    return true;
                }
            }
            return false;
        }
        createRequest(command, args) {
            return {
                seq: this.sequenceNumber++,
                type: 'request',
                command: command,
                arguments: args
            };
        }
    }
    class ForkedTsServerProcess {
        constructor(
        //private childProcess: cp.ChildProcess
        ) {
            this.hub = (window["Hub"]).typescript;
        }
        onError(cb) {
            this.hub.on("typescriptServerError", cb);
            //this.childProcess.on('error', cb);
        }
        onExit(cb) {
            this.hub.on("typescriptServerExit", cb);
            //this.childProcess.on('exit', cb);
        }
        write(serverRequest) {
            this.hub.call("write", JSON.stringify(serverRequest) + '\r\n');
            //this.childProcess.stdin.write(JSON.stringify(serverRequest) + '\r\n', 'utf8');
        }
        createReader(callback, onError = () => ({})) {
            // tslint:disable-next-line:no-unused-expression
            this.hub.on("typescriptServerData", (data) => {
                if (data.success) {
                    callback(data.content);
                    return;
                }
                onError(data.error);
            });
            //new Reader<any>(this.childProcess.stdout, callback, onError);
        }
        kill() {
            this.hub.call("kill");
            //this.childProcess.kill();
        }
    }
    class TypeScriptServiceClient {
        constructor(host, plugins, vscode) {
            this.host = host;
            this.plugins = plugins;
            this.vscode = vscode;
            this.tsServerLogFile = null;
            this.isRestarting = false;
            this.cancellationPipeName = null;
            /**
             * Version reported by currently-running tsserver.
             */
            //private _tsserverVersion: string | undefined;
            this.disposables = [];
            this._onTsServerStarted = new vscode.EventEmitter();
            this._onProjectLanguageServiceStateChanged = new vscode.EventEmitter();
            this._onDidBeginInstallTypings = new vscode.EventEmitter();
            this._onDidEndInstallTypings = new vscode.EventEmitter();
            this._onTypesInstallerInitializationFailed = new vscode.EventEmitter();
            this.pathSeparator = path.sep;
            this.lastStart = Date.now();
            var p = new Promise((resolve, reject) => {
                this._onReady = { promise: p, resolve, reject };
            });
            this._onReady.promise = p;
            this.servicePromise = null;
            this.lastError = null;
            this.numberRestarts = 0;
            this.requestQueue = new RequestQueue();
            this.callbacks = new CallbackMap();
            this._configuration = configuration_1.TypeScriptServiceConfiguration.loadFromWorkspace(vscode);
            this._apiVersion = api_1.default.defaultVersion;
            //this._tsserverVersion = undefined;
            // vscode.workspace.onDidChangeConfiguration(() => {
            // 	const oldConfiguration = this._configuration;
            // 	this._configuration = TypeScriptServiceConfiguration.loadFromWorkspace();
            // 	this.versionProvider.updateConfiguration(this._configuration);
            // 	this.tracer.updateConfiguration();
            // 	if (this.servicePromise) {
            // 		if (this._configuration.checkJs !== oldConfiguration.checkJs
            // 			|| this._configuration.experimentalDecorators !== oldConfiguration.experimentalDecorators
            // 		) {
            // 			this.setCompilerOptionsForInferredProjects(this._configuration);
            // 		}
            // 		if (!this._configuration.isEqualTo(oldConfiguration)) {
            // 			this.restartTsServer();
            // 		}
            // 	}
            // }, this, this.disposables);
        }
        get configuration() {
            return this._configuration;
        }
        dispose() {
            if (this.servicePromise) {
                this.servicePromise.then((childProcess) => {
                    childProcess.kill();
                }).then(undefined, () => void 0);
            }
            while (this.disposables.length) {
                const obj = this.disposables.pop();
                if (obj) {
                    obj.dispose();
                }
            }
        }
        // public restartTsServer(): void {
        // 	const start = () => {
        // 		this.servicePromise = this.startService(true);
        // 		return this.servicePromise;
        // 	};
        // 	if (this.servicePromise) {
        // 		this.servicePromise = this.servicePromise.then((childProcess: any) => {
        // 			this.info('Killing TS Server');
        // 			this.isRestarting = true;
        // 			childProcess.kill();
        // 			this.resetClientVersion();
        // 		}).then(start);
        // 	} else {
        // 		start();
        // 	}
        // }
        get onTsServerStarted() {
            return this._onTsServerStarted.event;
        }
        get onProjectLanguageServiceStateChanged() {
            return this._onProjectLanguageServiceStateChanged.event;
        }
        get onDidBeginInstallTypings() {
            return this._onDidBeginInstallTypings.event;
        }
        get onDidEndInstallTypings() {
            return this._onDidEndInstallTypings.event;
        }
        get onTypesInstallerInitializationFailed() {
            return this._onTypesInstallerInitializationFailed.event;
        }
        get apiVersion() {
            return this._apiVersion;
        }
        onReady(f) {
            return this._onReady.promise.then(f);
        }
        info(message, data) {
            console.info(message, data);
        }
        warn(message, data) {
            console.warn(message, data);
        }
        error(message, data) {
            console.error(message, data);
        }
        logTelemetry(eventName, properties) {
            console.info(eventName, properties);
        }
        service() {
            if (this.servicePromise) {
                return this.servicePromise;
            }
            if (this.lastError) {
                return Promise.reject(this.lastError);
            }
            this.startService();
            if (this.servicePromise) {
                return this.servicePromise;
            }
            return Promise.reject(new Error('Could not create TS service'));
        }
        ensureServiceStarted() {
            if (!this.servicePromise) {
                this.startService();
            }
        }
        startService(resendModels = false) {
            // let currentVersion = this.versionPicker.currentVersion;
            // this.info(`Using tsserver from: ${currentVersion.path}`);
            // if (!fs.existsSync(currentVersion.tsServerPath)) {
            // 	window.showWarningMessage(localize('noServerFound', 'The path {0} doesn\'t point to a valid tsserver install. Falling back to bundled TypeScript version.', currentVersion.path));
            // 	this.versionPicker.useBundledVersion();
            // 	currentVersion = this.versionPicker.currentVersion;
            // }
            // this._apiVersion = this.versionPicker.currentVersion.version || API.defaultVersion;
            // this.onDidChangeTypeScriptVersion(currentVersion);
            this.requestQueue = new RequestQueue();
            this.callbacks = new CallbackMap();
            this.lastError = null;
            var hub = (window["Hub"]).typescript;
            return this.servicePromise = hub.call("startServer").then(() => {
                const handle = new ForkedTsServerProcess();
                this.lastStart = Date.now();
                handle.onError((err) => {
                    this.lastError = err;
                    this.error('TSServer errored with error.', err);
                    if (this.tsServerLogFile) {
                        this.error(`TSServer log file: ${this.tsServerLogFile}`);
                    }
                    /* __GDPR__
                        "tsserver.error" : {}
                    */
                    this.logTelemetry('tsserver.error');
                    this.serviceExited(false);
                });
                handle.onExit((code) => {
                    if (code === null || typeof code === 'undefined') {
                        this.info('TSServer exited');
                    }
                    else {
                        this.error(`TSServer exited with code: ${code}`);
                        /* __GDPR__
                            "tsserver.exitWithCode" : {
                                "code" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
                            }
                        */
                        this.logTelemetry('tsserver.exitWithCode', { code: code });
                    }
                    if (this.tsServerLogFile) {
                        this.info(`TSServer log file: ${this.tsServerLogFile}`);
                    }
                    this.serviceExited(!this.isRestarting);
                    this.isRestarting = false;
                });
                handle.createReader(msg => { this.dispatchMessage(msg); }, error => { this.error('ReaderError', error); });
                this._onReady.resolve();
                //resolve(handle);
                this._onTsServerStarted.fire();
                this.serviceStarted(resendModels);
                return handle;
            }, (msg) => {
                console.error(msg);
                alert(msg);
            });
            // new Promise<ForkedTsServerProcess>(async (resolve, reject) => {
            // 	try {
            // 		const tsServerForkArgs = await this.getTsServerArgs(currentVersion);
            // 		const debugPort = this.getDebugPort();
            // 		const tsServerForkOptions: electron.IForkOptions = {
            // 			execArgv: debugPort ? [`--inspect=${debugPort}`] : [] // [`--debug-brk=5859`]
            // 		};
            // 		electron.fork(currentVersion.tsServerPath, tsServerForkArgs, tsServerForkOptions, this.logger, (err: any, childProcess: cp.ChildProcess | null) => {
            // 			if (err || !childProcess) {
            // 				this.lastError = err;
            // 				this.error('Starting TSServer failed with error.', err);
            // 				window.showErrorMessage(localize('serverCouldNotBeStarted', 'TypeScript language server couldn\'t be started. Error message is: {0}', err.message || err));
            // 				/* __GDPR__
            // 					"error" : {}
            // 				*/
            // 				this.logTelemetry('error');
            // 				this.resetClientVersion();
            // 				return;
            // 			}
            // 			this.info('Started TSServer');
            // 			const handle = new ForkedTsServerProcess(childProcess);
            // 			this.lastStart = Date.now();
            // 			handle.onError((err: Error) => {
            // 				this.lastError = err;
            // 				this.error('TSServer errored with error.', err);
            // 				if (this.tsServerLogFile) {
            // 					this.error(`TSServer log file: ${this.tsServerLogFile}`);
            // 				}
            // 				/* __GDPR__
            // 					"tsserver.error" : {}
            // 				*/
            // 				this.logTelemetry('tsserver.error');
            // 				this.serviceExited(false);
            // 			});
            // 			handle.onExit((code: any) => {
            // 				if (code === null || typeof code === 'undefined') {
            // 					this.info('TSServer exited');
            // 				} else {
            // 					this.error(`TSServer exited with code: ${code}`);
            // 					/* __GDPR__
            // 						"tsserver.exitWithCode" : {
            // 							"code" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
            // 						}
            // 					*/
            // 					this.logTelemetry('tsserver.exitWithCode', { code: code });
            // 				}
            // 				if (this.tsServerLogFile) {
            // 					this.info(`TSServer log file: ${this.tsServerLogFile}`);
            // 				}
            // 				this.serviceExited(!this.isRestarting);
            // 				this.isRestarting = false;
            // 			});
            // 			handle.createReader(
            // 				msg => { this.dispatchMessage(msg); },
            // 				error => { this.error('ReaderError', error); });
            // 			this._onReady!.resolve();
            // 			resolve(handle);
            // 			this._onTsServerStarted.fire();
            // 			this.serviceStarted(resendModels);
            // 		});
            // 	} catch (error) {
            // 		reject(error);
            // 	}
            // });
        }
        // public onVersionStatusClicked(): Promise<void> {
        // 	return this.showVersionPicker(false);
        // }
        // private showVersionPicker(firstRun: boolean): Promise<void> {
        // 	return this.versionPicker.show(firstRun).then(change => {
        // 		if (firstRun || !change.newVersion || !change.oldVersion || change.oldVersion.path === change.newVersion.path) {
        // 			return;
        // 		}
        // 		this.restartTsServer();
        // 	});
        // }
        serviceStarted(resendModels) {
            const configureOptions = {
                hostInfo: 'vscode'
            };
            this.execute('configure', configureOptions);
            this.setCompilerOptionsForInferredProjects(this._configuration);
            if (resendModels) {
                this.host.populateService();
            }
        }
        setCompilerOptionsForInferredProjects(configuration) {
            if (!this.apiVersion.has206Features()) {
                return;
            }
            const args = {
                options: this.getCompilerOptionsForInferredProjects(configuration)
            };
            this.execute('compilerOptionsForInferredProjects', args, true);
        }
        getCompilerOptionsForInferredProjects(configuration) {
            return Object.assign({}, tsconfig_1.inferredProjectConfig(configuration), { allowJs: true, allowSyntheticDefaultImports: true, allowNonTsExtensions: true });
        }
        serviceExited(restart) {
            // enum MessageAction {
            // 	reportIssue
            // }
            this.servicePromise = null;
            this.tsServerLogFile = null;
            this.callbacks.destroy(new Error('Service died.'));
            this.callbacks = new CallbackMap();
            if (!restart) {
                //this.resetClientVersion();
            }
            else {
                const diff = Date.now() - this.lastStart;
                this.numberRestarts++;
                let startService = true;
                if (this.numberRestarts > 5) {
                    //let prompt: Promise<any | undefined> | undefined = undefined;
                    this.numberRestarts = 0;
                    if (diff < 10 * 1000 /* 10 seconds */) {
                        this.lastStart = Date.now();
                        startService = false;
                        // prompt = window.showErrorMessage<MyMessageItem>(
                        // 	localize('serverDiedAfterStart', 'The TypeScript language service died 5 times right after it got started. The service will not be restarted.'),
                        // 	{
                        // 		title: localize('serverDiedReportIssue', 'Report Issue'),
                        // 		id: MessageAction.reportIssue,
                        // 		isCloseAffordance: true
                        // 	});
                        /* __GDPR__
                            "serviceExited" : {}
                        */
                        this.logTelemetry('serviceExited');
                        //this.resetClientVersion();
                    }
                    else if (diff < 60 * 1000 /* 1 Minutes */) {
                        this.lastStart = Date.now();
                        // prompt = window.showWarningMessage<MyMessageItem>(
                        // 	localize('serverDied', 'The TypeScript language service died unexpectedly 5 times in the last 5 Minutes.'),
                        // 	{
                        // 		title: localize('serverDiedReportIssue', 'Report Issue'),
                        // 		id: MessageAction.reportIssue,
                        // 		isCloseAffordance: true
                        // 	});
                    }
                    // if (prompt) {
                    // 	prompt.then(item => {
                    // 		if (item && item.id === MessageAction.reportIssue) {
                    // 			return commands.executeCommand('workbench.action.reportIssues');
                    // 		}
                    // 		return undefined;
                    // 	});
                    // }
                }
                if (startService) {
                    this.startService(true);
                }
            }
        }
        normalizePath(resource) {
            if (this._apiVersion.has213Features()) {
                if (resource.scheme === fileSchemes.walkThroughSnippet || resource.scheme === fileSchemes.untitled) {
                    const dirName = path.dirname(resource.path);
                    const fileName = this.inMemoryResourcePrefix + path.basename(resource.path);
                    return resource.with({ path: path.join(dirName, fileName) }).toString(true);
                }
            }
            if (resource.scheme !== fileSchemes.file) {
                return null;
            }
            const result = resource.fsPath;
            if (!result) {
                return null;
            }
            // Both \ and / must be escaped in regular expressions
            return result.replace(new RegExp('\\' + this.pathSeparator, 'g'), '/');
        }
        get inMemoryResourcePrefix() {
            return this._apiVersion.has270Features() ? '^' : '';
        }
        asUrl(filepath) {
            if (this._apiVersion.has213Features()) {
                if (filepath.startsWith(TypeScriptServiceClient.WALK_THROUGH_SNIPPET_SCHEME_COLON) || (filepath.startsWith(fileSchemes.untitled + ':'))) {
                    let resource = this.vscode.Uri.parse(filepath);
                    if (this.inMemoryResourcePrefix) {
                        const dirName = path.dirname(resource.path);
                        const fileName = path.basename(resource.path);
                        if (fileName.startsWith(this.inMemoryResourcePrefix)) {
                            resource = resource.with({ path: path.join(dirName, fileName.slice(this.inMemoryResourcePrefix.length)) });
                        }
                    }
                    return resource;
                }
            }
            return this.vscode.Uri.file(filepath);
        }
        getWorkspaceRootForResource(resource) {
            const roots = this.vscode.workspace.workspaceFolders;
            if (!roots || !roots.length) {
                return undefined;
            }
            if (resource.scheme === fileSchemes.file || resource.scheme === fileSchemes.untitled) {
                for (const root of roots.sort((a, b) => a.uri.fsPath.length - b.uri.fsPath.length)) {
                    if (resource.fsPath.startsWith(root.uri.fsPath + path.sep)) {
                        return root.uri.fsPath;
                    }
                }
                return roots[0].uri.fsPath;
            }
            return undefined;
        }
        execute(command, args, expectsResultOrToken) {
            let token = undefined;
            let expectsResult = true;
            if (typeof expectsResultOrToken === 'boolean') {
                expectsResult = expectsResultOrToken;
            }
            else {
                token = expectsResultOrToken;
            }
            const request = this.requestQueue.createRequest(command, args);
            const requestInfo = {
                request: request,
                callbacks: null
            };
            let result;
            if (expectsResult) {
                let wasCancelled = false;
                result = new Promise((resolve, reject) => {
                    requestInfo.callbacks = { c: resolve, e: reject, start: Date.now() };
                    if (token) {
                        token.onCancellationRequested(() => {
                            wasCancelled = true;
                            this.tryCancelRequest(request.seq);
                        });
                    }
                }).catch((err) => {
                    if (!wasCancelled) {
                        this.error(`'${command}' request failed with error.`, err);
                        const properties = this.parseErrorText(err && err.message, command);
                        this.logTelemetry('languageServiceErrorResponse', properties);
                    }
                    throw err;
                });
            }
            else {
                result = Promise.resolve(null);
            }
            this.requestQueue.push(requestInfo);
            this.sendNextRequests();
            return result;
        }
        /**
         * Given a `errorText` from a tsserver request indicating failure in handling a request,
         * prepares a payload for telemetry-logging.
         */
        parseErrorText(errorText, command) {
            const properties = Object.create(null);
            properties['command'] = command;
            if (errorText) {
                properties['errorText'] = errorText;
                const errorPrefix = 'Error processing request. ';
                if (errorText.startsWith(errorPrefix)) {
                    const prefixFreeErrorText = errorText.substr(errorPrefix.length);
                    const newlineIndex = prefixFreeErrorText.indexOf('\n');
                    if (newlineIndex >= 0) {
                        // Newline expected between message and stack.
                        properties['message'] = prefixFreeErrorText.substring(0, newlineIndex);
                        properties['stack'] = prefixFreeErrorText.substring(newlineIndex + 1);
                    }
                }
            }
            return properties;
        }
        sendNextRequests() {
            while (this.callbacks.pendingResponses === 0 && this.requestQueue.length > 0) {
                const item = this.requestQueue.shift();
                if (item) {
                    this.sendRequest(item);
                }
            }
        }
        sendRequest(requestItem) {
            const serverRequest = requestItem.request;
            if (requestItem.callbacks) {
                this.callbacks.add(serverRequest.seq, requestItem.callbacks);
            }
            this.service()
                .then((childProcess) => {
                childProcess.write(serverRequest);
            })
                .then(undefined, err => {
                const callback = this.callbacks.fetch(serverRequest.seq);
                if (callback) {
                    callback.e(err);
                }
            });
        }
        tryCancelRequest(seq) {
            try {
                if (this.requestQueue.tryCancelPendingRequest(seq)) {
                    //login.logTrace(`TypeScript Service: canceled request with sequence number ${seq}`);
                    return true;
                }
                if (this.apiVersion.has222Features() && this.cancellationPipeName) {
                    //this.tracer.logTrace(`TypeScript Service: trying to cancel ongoing request with sequence number ${seq}`);
                    try {
                        //fs.writeFileSync(this.cancellationPipeName + seq, '');
                    }
                    catch (_a) {
                        // noop
                    }
                    return true;
                }
                //this.tracer.logTrace(`TypeScript Service: tried to cancel request with sequence number ${seq}. But request got already delivered.`);
                return false;
            }
            finally {
                const p = this.callbacks.fetch(seq);
                if (p) {
                    p.e(new Error(`Cancelled Request ${seq}`));
                }
            }
        }
        dispatchMessage(message) {
            try {
                if (message.type === 'response') {
                    const response = message;
                    const p = this.callbacks.fetch(response.request_seq);
                    if (p) {
                        //this.tracer.traceResponse(response, p.start);
                        if (response.success) {
                            p.c(response);
                        }
                        else {
                            p.e(response);
                        }
                    }
                }
                else if (message.type === 'event') {
                    const event = message;
                    //this.tracer.traceEvent(event);
                    this.dispatchEvent(event);
                }
                else {
                    throw new Error('Unknown message type ' + message.type + ' recevied');
                }
            }
            finally {
                this.sendNextRequests();
            }
        }
        dispatchEvent(event) {
            switch (event.event) {
                case 'syntaxDiag':
                    this.host.syntaxDiagnosticsReceived(event);
                    break;
                case 'semanticDiag':
                    this.host.semanticDiagnosticsReceived(event);
                    break;
                case 'configFileDiag':
                    this.host.configFileDiagnosticsReceived(event);
                    break;
                case 'telemetry':
                    const telemetryData = event.body;
                    this.dispatchTelemetryEvent(telemetryData);
                    break;
                case 'projectLanguageServiceState':
                    if (event.body) {
                        this._onProjectLanguageServiceStateChanged.fire(event.body);
                    }
                    break;
                case 'beginInstallTypes':
                    if (event.body) {
                        this._onDidBeginInstallTypings.fire(event.body);
                    }
                    break;
                case 'endInstallTypes':
                    if (event.body) {
                        this._onDidEndInstallTypings.fire(event.body);
                    }
                    break;
                case 'typesInstallerInitializationFailed':
                    if (event.body) {
                        this._onTypesInstallerInitializationFailed.fire(event.body);
                    }
                    break;
            }
        }
        dispatchTelemetryEvent(telemetryData) {
            const properties = Object.create(null);
            switch (telemetryData.telemetryEventName) {
                case 'typingsInstalled':
                    const typingsInstalledPayload = telemetryData.payload;
                    properties['installedPackages'] = typingsInstalledPayload.installedPackages;
                    if (is.defined(typingsInstalledPayload.installSuccess)) {
                        properties['installSuccess'] = typingsInstalledPayload.installSuccess.toString();
                    }
                    if (is.string(typingsInstalledPayload.typingsInstallerVersion)) {
                        properties['typingsInstallerVersion'] = typingsInstalledPayload.typingsInstallerVersion;
                    }
                    break;
                default:
                    const payload = telemetryData.payload;
                    if (payload) {
                        Object.keys(payload).forEach((key) => {
                            try {
                                if (payload.hasOwnProperty(key)) {
                                    properties[key] = is.string(payload[key]) ? payload[key] : JSON.stringify(payload[key]);
                                }
                            }
                            catch (e) {
                                // noop
                            }
                        });
                    }
                    break;
            }
            // if (telemetryData.telemetryEventName === 'projectInfo') {
            // 	this._tsserverVersion = properties['version'];
            // }
            /* __GDPR__
                "typingsInstalled" : {
                    "installedPackages" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
                    "installSuccess": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
                    "typingsInstallerVersion": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
                }
            */
            // __GDPR__COMMENT__: Other events are defined by TypeScript.
            this.logTelemetry(telemetryData.telemetryEventName, properties);
        }
    }
    TypeScriptServiceClient.WALK_THROUGH_SNIPPET_SCHEME_COLON = `${fileSchemes.walkThroughSnippet}:`;
    exports.default = TypeScriptServiceClient;
});
// const getTsLocale = (configuration: TypeScriptServiceConfiguration): string | undefined =>
// 	(configuration.locale
// 		? configuration.locale
// 		: env.language);
